/*
*@author diqye
*@version 0.0.1
*/
(function(exports){
    function headStr(str){
        return str == null ? null
            : str == "" ? ""
            : str.charAt(0);
    }
    function lastStr(str){
        return str == null ? null
            : str == "" ? ""
            : str.charAt(str.length-1);
    }
    function tailStr(str){
        return str == null ? null
            : str == "" ? ""
            : str.slice(1);
    }
    function initStr(str){
        return str == null ? null
            : str == "" ? ""
            : str.slice(0,-1);
    }
    function findIdxStr(flg,str){
        return str == null ? null
            :str.indexOf(flg);
    }
    function takeStr(len,str){
        return str.slice(0,len);
    }
    function dropStr(len,str){
        return str.slice(len);
    }
    function drop(len,arr){
        if(typeof arr === "string") return dropStr(len,arr);
        return arr.slice(len);
    }
    function isEmptyStr(str){
        return str === "" ? true
            : false;
    }
    function isStringToken(token){
        var hstr = headStr(token);
        var lstr = lastStr(token);
        if(hstr === '"'){
            if(lstr === '"'){
                return true;
            }else{
                return false;
            }
        }else if(hstr === "'"){
            if(lstr === "'"){
                return true;
            }else{
                return false;
            }
        }else{
            return false;
        }
    }
    function stringTokenToData(token){
        return initStr(tailStr(token));
    }
    function isNumberToken(token){
        return isNaN(Number(token)) == false;
    }
    function numberTokenToData(token){
        return Number(token);
    }
    function isBooleanToken(token){
        return token == "true" ? true
            : token == "false"  ? true
            :false;
    }
    function boolTokenToData(token){
        return token == "true";
    }
    function isEmptyArrToken(token){
        return token  == "[]";
    }
    function emptyArrTokenToData(){
        return {type:"q",value:isEmptyArr};
    }
    function isEmptyObjToken(token){
        return token == "{}";
    }
    function emptyObjTokenToData(){
        return {type:"q",value:isEmptyObj};
    }
    function isEmptyArr(arr){
        return arr == null ? false
            : arr.length == 0 ? true
            : false
    }
    function isEmptyObj(obj){
        if(obj == null) return false;
        if(isobject(obj) == false) return false;
        var r = true;
        for(var key in obj)r=false;
        return r;
    }
    function isNullToken(token){
        return token == "null";
    }
    function isIgnoreToken(token){
        return token == "_";
    }
    function ignoreTokenToData(){
        return {type:"ignore",value:"_"};
    }
    function isIgnoreType(obj){
        return obj.type === "ignore";
    }
    function isArrayToken(token){
        var hstr = headStr(token);
        var lstr = lastStr(token);
        if(hstr=="@" && lstr == ")"){
            return true;
        }
        return hstr == "(" && lstr == ")";
    }
    function arrayTokenToData(token){
        var hstr = headStr(token);
        var all = null;
        var arrtoken=token;
        if(hstr == "@"){
            var t1 = tailStr(token);
            var idx = findIdxStr("(",t1);
            all = symbalTokenToData(trim(takeStr(idx,t1)));
            arrtoken = dropStr(idx,t1);
        } 
        var t2 = tailStr(arrtoken);
        var tend = initStr(t2);
        var result = parsecase(tend,"",{},"array");
        return {type:"array",value:result,all:all};
    }
    function symbalTokenToData(token){
        if(token.indexOf(" ") != -1){
            throw new SyntaxError(token+":不是合法的符号命名    Illegal naming");
        }
        var qidx = token.indexOf("?");
        if(qidx != -1){
            var val = takeStr(qidx,token);
            var qfn = dropStr(qidx+1,token);
            return {
                type:"symbal",
                value:val,
                qfn:qfn
            }
        }
        return {
            type:"symbal",
            value:token
        }
    }
    function isSymbalType(a){
        return a == null ? false
            : a.type == "symbal" ? true
            :false;
    }
    function isObjectToken(token){
        var hstr = headStr(token);
        var lstr = lastStr(token);
        if(hstr=="@" && lstr == "}"){
            return true;
        }
        return hstr == "{" && lstr == "}";
    }
    function objectTokenToData(token){
        var hstr = headStr(token);
        var all = null;
        var arrtoken=token;
        if(hstr == "@"){
            var t1 = tailStr(token);
            var idx = findIdxStr("{",t1);
            var alltoken = takeStr(idx,t1);
            all = symbalTokenToData(trim(alltoken));
            arrtoken = dropStr(idx,t1);
        } 
        var t2 = tailStr(arrtoken);
        var tend = initStr(t2);
        var result = parsecase(tend,"",{},"object");
        var tempvarr=[];
        var valueReult=[];
        function checkObjSynctax(tempvarr){
            if(tempvarr.length == 0){
                throw new SyntaxError(token+":对象语法错误");
            }else if(tempvarr.length ==1){
                var tmp1 = tempvarr[0];
                if(isSymbalType(tmp1)) tempvarr.push(tmp1);
                else new SyntaxError(token+":对象语法错误");
            }else if(tempvarr.length == 2){
                void null;
            }else{
                throw new SyntaxError(token+":对象语法错误");
            }
        }
        for(var i=0;i<result.length;i++){
            var item = result[i];
            if(isHolder(item)){
                checkObjSynctax(tempvarr);
                valueReult.push(tempvarr);
                tempvarr=[];
                continue;
            }
            tempvarr.push(item);
        }
        checkObjSynctax(tempvarr);
        valueReult.push(tempvarr);
        return {type:"object",value:valueReult,all:all};
    }
    function isHolder(obj){
        return obj["@holder@"]=="|";
    }
    function tokenToData(token){
        return isBooleanToken(token) ? boolTokenToData(token)
            : isStringToken(token) ? stringTokenToData(token)
            : isNumberToken(token) ? numberTokenToData(token)
            : isEmptyArrToken(token) ? emptyArrTokenToData(token)
            : isEmptyObjToken(token) ? emptyObjTokenToData(token)
            : isArrayToken(token) ? arrayTokenToData(token)
            : isObjectToken(token) ? objectTokenToData(token)
            : isNullToken(token) ? null
            : isIgnoreToken(token) ? ignoreTokenToData(token)
            : symbalTokenToData(token)
    }
    function trim(str){
        if(headStr(str) == " "){
            return trim(tailStr(str));
        }else if(lastStr(str) == " "){
            return trim(initStr(str));
        }else{
            return str;
        }
    }
    function parsecase(casetoken,token,ctx,otype){
        var head = headStr(casetoken);
        function makeSymbal(entityToken){
            entityToken = trim(entityToken);
            return isEmptyStr(entityToken) ? []
                : [tokenToData(entityToken)];
        }
        function endSyncMakeSymbal(entityToken){
            if(ctx.end != null){
                throw new SyntaxError(entityToken+":类型"+ctx.type+"缺少结束标志"+ctx.end
                                    +"   the type "+ctx.type+" miss end flag "+ctx.end);
            }
            return makeSymbal(entityToken);
        }
        function ignoreSpaceCall(){
            return ctx.type == "string" ? parsecase(tailStr(casetoken),token+head,ctx,otype)
                : parsecase(tailStr(casetoken),token,ctx,otype);
        }
        function restartCall(){
            return parsecase(tailStr(casetoken),"",{},otype);
        }
        function endFlag(){
            if(ctx.type2=="string"){
                return parsecase(tailStr(casetoken),token+head,ctx,otype);
            }else if(ctx.endNum==1){
                return makeSymbal(token+head).concat(restartCall());
            }else{
                ctx.endNum--;
                return parsecase(tailStr(casetoken),token+head,ctx,otype);
            }
        }
        function doIgnoreString(type,startFlag){
            if(ctx.type==type){
                if(ctx.type2=="string"){
                    if(ctx.end2==head){
                        ctx.type2=null;
                    }else{
                    }
                }else if(head==startFlag){
                    ctx.endNum++;
                }else if(head =="'"){
                    ctx.type2="string";
                    ctx.end2="'";
                }else if(head=='"'){
                    ctx.type2="string";
                    ctx.end2='"';
                }else{
                }
            }
        }
        doIgnoreString("array","(");
        doIgnoreString("object","{");
        //{key:11,key2:'33',key3:x,key4:y}
        return isEmptyStr(casetoken) ? endSyncMakeSymbal(token)
            : ctx.end==null&&head === "'" ? parsecase(tailStr(casetoken),token+head,{type:"string",end:"'",endNum:1},otype)
            : ctx.end==null&&head === '"' ? parsecase(tailStr(casetoken),token+head,{type:"string",end:'"',endNum:1},otype)
            : ctx.end==null&&head === '(' ? parsecase(tailStr(casetoken),token+head,{type:"array",end:')',endNum:1},otype)
            : ctx.end==null&&head === '{' ? parsecase(tailStr(casetoken),token+head,{type:"object",end:'}',endNum:1},otype)
            : otype=="array"&&ctx.end==null&&head === ":" ? makeSymbal(token).concat(restartCall())
            : otype=="object"&&ctx.end==null&&head==":" ? makeSymbal(token).concat(restartCall())
            : otype=="object"&&ctx.end==null&&head=="," ? makeSymbal(token).concat({"@holder@":"|"}).concat(restartCall())
            : head === ctx.end ? endFlag()
            : ctx.end==null&&head === "," ? makeSymbal(token).concat(restartCall())
            : parsecase(tailStr(casetoken),token+head,ctx,otype);
    }
    function toArray(a){
        return [].slice.call(a,0);
    }
    //=============array object type=====
    function isArrayType(obj){
        return obj.type == "array";
    }
    function isQType(obj){
        return obj.type == "q";
    }
    function qtest(obj,test){
        return obj.value(test);
    }
    function getArrayAll(obj){
        return obj.all;
    }
    function getArrayVal(obj){
        return obj.value;
    }
    function isObjectType(obj){
        return obj.type =="object";
    }
    function getObjectVal(obj){
        return obj.value;
    }
    function getObjectAll(obj){
        return obj.all;
    }
    //=============
    function matchcase(casetoken,scope){
        function getJiegouKeyString(a){
            return typeof a === "string" ? a : a.value;
        }
        function jiegouObject(objarr,obj){
            if(obj == null)return false;
            if(typeof obj !== "object")return false;
            var jarr = [];
            for(var i=0;i<objarr.length;i++){
                var item = objarr[i];
                var key = getJiegouKeyString(item[0]);
                var objval = obj[key];
                var rarr= domatch([item[1]],[objval],0,0,"object");
                if(rarr === false)return false;
                jarr=jarr.concat(rarr);
            }
            return jarr;
        }
        var casearr = parsecase(casetoken,"",{},"none");
        function notListType(obj){
            return typeof obj === "string" ? false
                : Object.prototype.toString.call(obj)==="[object Array]" ? false
                : true;
        }
        function domatch(casearr,args,ai,ci,otype){
            ai=ai||0;
            ci=ci||0;
            if(casearr.length == ci) return [];
            var arg = typeof args == "string" ? args.charAt(ai) : args[ai];
            if(otype == "array"){
                if(ci == casearr.length-1){
                    arg = drop(ai,args);
                }
                if(arg === undefined){
                    if(ai>=args.length)return false;
                }
            }
            var crg = casearr[ci];
            if(typeof crg !== "object"){
                if(arg === crg) return domatch(casearr,args,ai+1,ci+1,otype);
                else return false;
            }else if(crg == null){
                if(arg == null) return domatch(casearr,args,ai+1,ci+1,otype);
                else return false;
            }else if(isSymbalType(crg)){
                if(crg.qfn != null){
                    if(!symbalQfn(crg.qfn,arg,scope)) return false;
                }
                var tr = domatch(casearr,args,ai+1,ci+1,otype)
                if(tr === false) return false;
                else return [arg].concat(tr);
            }else if(isIgnoreType(crg)){
                return domatch(casearr,args,ai+1,ci+1,otype)
            }else if(isArrayType(crg)){
                if(notListType(arg))return false;
                var all = getArrayAll(crg);
                var rarr = [];
                if(all != null){
                    var allval = domatch([all],[arg],0,0,"none");
                    if(allval === false)return false;
                    rarr=rarr.concat(allval);
                }
                var subarr = domatch(getArrayVal(crg),arg,0,0,"array");
                if(subarr === false) return false;
                var endarr = domatch(casearr,args,ai+1,ci+1,otype);
                if(endarr === false) return false;
                return rarr.concat(subarr).concat(endarr);
            }else if(isQType(crg)){
                if(qtest(crg,arg)) return domatch(casearr,args,ai+1,ci+1,otype);
                else return false;
            }else if(isObjectType(crg)){
                var all = getObjectAll(crg);
                var oarr = [];
                if(all != null){
                    var oarrval = domatch([all],[arg],0,0,"none");
                    if(oarrval === false)return false;
                    oarr=oarr.concat(oarrval);
                }
                var arr1 = jiegouObject(getObjectVal(crg),arg);
                if(arr1 === false)return false;
                var arr2 = domatch(casearr,args,ai+1,ci+1,otype);
                return arr2 === false ? false : oarr.concat(arr1).concat(arr2);
            }else{
                return false;
            }
        }
        return function(){
            var args = toArray(arguments);
            return args.length !== casearr.length ? false
                : domatch(casearr,args);
        }
    }
    function defun(matchObj,scope){
        return function(){
            var args = toArray(arguments);
            for(key in matchObj){
                var val = matchObj[key];
                if(key == "else"){
                    if(typeof val !== "function") return val;
                    return val.apply(null,args);
                }
                var nargs = matchcase(key,scope).apply(null,args);
                if(nargs === false) continue;
                if(typeof val !== "function") return val;
                return val.apply(null,nargs);
            }
        }
    }
    function symbalQfn(fnname,arg,scope){
        return scope != null&&typeof scope[fnname] == "function" ? scope[fnname](arg)
            : typeof defun.globalScope[fnname] == "function" ? defun.globalScope[fnname](arg)
            : typeof exports[fnname] == "function" ? exports[fnname](arg)
            : false;
    }
    // scope function
    function isarray(a){
        return a == null ? false
            : Object.prototype.toString.call(a) == "[object Array]";
    }
    function isobject(a){
        return a == null ? false
            :Object.prototype.toString.call(a) == "[object Object]";
    }
    function isnumber(a){
        return typeof a == "number";
    }
    function isstring(a){
        return typeof a == "string";
    }
    function isdate(a){
        return a == null ? false
            :Object.prototype.toString.call(a) == "[object Date]";
    }
    defun.globalScope={
        array:isarray
        ,object:isobject
        ,number:isnumber
        ,string:isstring
        ,date:isdate
    };
    exports.defun=defun;
    defun.parsecase=parsecase;
}(this))
